Skip to content

feat: Fine-Grained Authorization with Keycloak-Core Policy Engine#607

Open
lakhansamani wants to merge 26 commits intomainfrom
feat/fine-grained-authorization
Open

feat: Fine-Grained Authorization with Keycloak-Core Policy Engine#607
lakhansamani wants to merge 26 commits intomainfrom
feat/fine-grained-authorization

Conversation

@lakhansamani
Copy link
Copy Markdown
Contributor

@lakhansamani lakhansamani commented Apr 14, 2026

Update (2026-04-23): Enforcement reduced from 3 modes to 2 — permissive (new default) and enforcing. The disabled mode is gone; existing deployments passing --authorization-enforcement=disabled are auto-migrated to permissive with a one-time INFO log, so no operator action is required. New Prometheus signals (authorizer_authz_checks_total, authorizer_authz_unmatched_total, authorizer_authz_check_duration_seconds) plus a rate-limited authz.unmatched warn log give operators the visibility needed to flip from permissiveenforcing once permissions are seeded. Watch authorizer_authz_unmatched_total{mode="permissive"} trend to zero before flipping. See docs/superpowers/specs/2026-04-21-authz-two-mode-enforcement-design.md.

Summary

Implements RFC #508 — Fine-Grained Authorization with a Keycloak-inspired four-pillar model (Resources, Scopes, Policies, Permissions). This replaces the flat comma-separated role strings with a composable, principal-agnostic authorization engine.

Key features

  • Resources — define what you're protecting (e.g., "document", "invoice")
  • Scopes — define allowed actions (e.g., "read", "write", "delete")
  • Policies — define who gets access via role-based or user-based conditions, with positive/negative logic
  • Permissions — bind resources + scopes + policies with affirmative or unanimous decision strategies
  • Policy Evaluation Engine — principal-agnostic (supports users, M2M clients, AI agents), with in-memory caching, negative caching, input validation, and 2 enforcement modes (permissive default / enforcing)
  • REST endpointPOST /api/v1/check-permission for downstream services
  • Dashboard UI — 5 pages (Resources, Scopes, Policies, Permissions, Evaluate) with natural language permission summaries and a policy evaluation test tool

Observability

  • Prometheus collectors: authorizer_authz_checks_total{mode,result}, authorizer_authz_unmatched_total{mode}, authorizer_authz_check_duration_seconds
  • Rate-limited authz.unmatched=true warn log (1 per (resource, scope) per 60s) on permissive fallthroughs
  • In-process per-(resource, scope) unmatched counter (foundation for future "Uncovered checks" dashboard view)
  • Startup banner: mode-aware INFO plus a loud WARN when booting enforcing with 0 permissions configured

Backward compatibility

  • Default is --authorization-enforcement=permissive — unmatched checks are ALLOWED and logged, so apps keep working while operators seed permissions
  • Legacy --authorization-enforcement=disabled is silently normalized to permissive with a one-time INFO log — no operator action required during upgrade
  • Existing User.Roles field, IsSuperAdmin() checks, and JWT format are unchanged
  • Permissions are only added to JWT when --include-permissions-in-token=true

Future-proof for M2M and AI agents

  • Principal abstraction accepts any identity type (user, client, agent)
  • MaxScopes delegation ceiling for token-scoped restrictions
  • Extensible policy types — adding "client" or "agent" is one switch case, no schema migration

Storage

  • 7 new tables across all 13 database providers (SQL via GORM, MongoDB, ArangoDB, Cassandra, Couchbase, DynamoDB)
  • 28 new storage interface methods per provider
  • Optimized GetPermissionsForResourceScope JOIN query for the evaluation hot path

New CLI flags

  • --authorization-enforcement (permissive default / enforcing; legacy disabled auto-migrated)
  • --authorization-cache-ttl (default: 300s)
  • --include-permissions-in-token (default: false)
  • --authorization-log-all-checks (default: false)

Test plan

  • 16 integration tests pass (SQLite): CRUD for all entities, permission evaluation with role-based policies, referential integrity on delete, cleanup
  • 8 new integration tests pass (SQLite): permissive-allow-no-data, enforcing-deny-no-data, explicit-deny-in-permissive-still-denies, Prometheus counter increment, and 4 TestConfig_* migration tests
  • go build ./... passes
  • go vet ./... passes
  • Dashboard npm run build passes
  • Manual testing: start with make dev, navigate to Authorization in dashboard, create resource/scope/policy/permission, test evaluate page
  • Test with --authorization-enforcement=enforcing to verify default-deny behavior
  • Test REST endpoint with curl -X POST /api/v1/check-permission
  • Smoke-boot with --authorization-enforcement=disabled to confirm the legacy-migration INFO log fires and the server boots in permissive mode

Design specs

  • docs/superpowers/specs/2026-04-13-fine-grained-authorization-design.md — original FGA design
  • docs/superpowers/specs/2026-04-21-authz-two-mode-enforcement-design.md — two-mode enforcement refactor

…methods

Add 7 new collection/table names (Resource, Scope, Policy, PolicyTarget,
Permission, PermissionScope, PermissionPolicy) to CollectionList and
Collections var.

Add 28 new method signatures to the Provider interface covering CRUD for
resources, scopes, policies, policy targets, permissions, and join tables,
plus the optimized GetPermissionsForResourceScope evaluation query.
Add CRUD methods for resources, scopes, policies, policy targets,
permissions, permission scopes, and permission policies. Implement
GetPermissionsForResourceScope optimized JOIN query for the evaluation
engine. Add 7 new schemas to AutoMigrate.
Add CRUD methods for all 7 authorization collections using MongoDB driver.
Implement GetPermissionsForResourceScope using sequential lookups.
Create indexes for efficient name and foreign key lookups.
…ders

Add authorization CRUD methods for all remaining NoSQL providers.
Each provider follows its existing patterns for collection access,
querying, and pagination. All 6 providers now implement the full
28-method authorization storage interface.
Add principal-agnostic policy evaluation engine with:
- Role-based and user-based policy evaluators (extensible to client/agent)
- Affirmative and unanimous decision strategies
- MaxScopes delegation ceiling enforcement
- Input validation (safe characters, known resource/scope checks)
- In-memory cache with negative caching and prefix invalidation
- Three enforcement modes (disabled, permissive, enforcing)
Add SetCache, GetCache, DeleteCacheByPrefix to Redis, DB-backed,
and in-memory memory store providers. Update fakeMemoryStore in
tests to implement the new interface methods.
Add --authorization-enforcement, --authorization-cache-ttl,
--include-permissions-in-token, --authorization-log-all-checks flags.
Wire authorization provider in cmd/root.go initialization order.
Pass authorization provider to http_handlers and graphql dependencies.
Add 10 output types, 9 input types, 12 admin mutations, and 6 queries
for the authorization model. Regenerate GraphQL code. Wire resolver
stubs with placeholder implementations until Phase 10.
Add 18 GraphQL handler methods: CRUD for resources, scopes, policies,
permissions, plus check_permission and my_permissions user-facing queries.
Add AsAPI conversion methods to all authorization schemas.
Wire resolvers to graphql Provider interface.
Add POST /api/v1/check-permission endpoint for downstream services.
Extracts principal from JWT Bearer token, evaluates permission via
authorization engine, returns {allowed, matched_policy} JSON response.
Add 16 integration tests covering resource/scope/policy/permission CRUD,
permission evaluation with role-based policies, referential integrity
checks on delete, and cleanup. All tests pass with SQLite.
Add 5 authorization pages to admin dashboard: Resources, Scopes,
Policies, Permissions, and Evaluate. Include guided setup flow,
natural language permission summaries, and policy evaluation
test tool. Add Authorization nav item to sidebar.
- H-1: Make cache invalidation synchronous (remove goroutine) to prevent
  stale authorization decisions after policy changes
- H-2: Add allowlist validation for policy type, logic, and decision
  strategy in add/update handlers -- prevents silent permission escalation
  from typos
- C-2: Fix evaluateRoleTargets unanimous strategy returning true when no
  role targets exist (empty-target bypass)
- C-3: Fix DeletePolicy handler ordering -- storage provider now handles
  cascade deletion, preventing data loss on referential integrity failure
- M-3: Add name format validation (alphanumeric, hyphens, underscores,
  max 100 chars) in resource/scope/policy/permission add/update handlers
Upgrade to latest available versions to address CVE-2026-33816 and
CVE-2026-33815 in pgx. Note: both CVEs have no upstream fix yet
(Fixed in: N/A) but govulncheck confirms the affected symbols
(Backend.Receive, FunctionCall.Decode, Bind.Decode) are not called
by Authorizer code. Also upgrades gorm v1.25.5->v1.25.10.
…fer cache decode, shared identifier constant
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant